/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#pragma once

#include <baja/common.hpp>
#include <baja/math.hpp>
#include <baja/transition_tracking/transition_tracking_interfaces.h>
#include <journal/common/settings_service.hpp>
#include <journal/common/performance_service.h>
#include <journal/common/storage_service_interfaces.h>
#include <journal/common/automation_service_interfaces.h>
#include <journal/common/memory_tracking_interfaces.h>

#include <journal/views/types.h>
#include <journal/models/types.h>

namespace journal {

namespace a = baja::app;
namespace b = baja;
namespace m = baja::math;
namespace g = baja::graphics;
namespace jss = journal::services::storage;
namespace jsa = journal::services::automation;
namespace bt = baja::transition_tracking;
namespace jsm = journal::services::memory_tracking;

#if 0
#define TRACE_LOCK(s) BAJA_TRACE(boost::format("~%i: %s") % GetCurrentThreadId() % s);
#else
#define TRACE_LOCK(s) 
#endif

// view lock
// TRACK: this would technically belong to a different header file but for simplicity we are parking it here
#define ACQUIRE_VIEW_LOCK \
    TRACE_LOCK("Acquiring view lock guard"); \
    std::lock_guard<std::recursive_mutex> __lockGuard(constants::_viewMutex);

#define ACQUIRE_VIEW_DEFERRED_LOCK \
    TRACE_LOCK("Acquiring view deferred lock guard"); \
    std::lock_guard<std::recursive_mutex> __lockGuard(constants::_viewDeferredMutex);

#define ACQUIRE_VIEW_LOCK_EXPLICIT \
    TRACE_LOCK("Acquiring view unique lock"); \
    std::unique_lock<std::recursive_mutex> __uniqueLock(constants::_viewMutex);

#define RELINQUISH_VIEW_LOCK \
    TRACE_LOCK("Relinquishing view unique lock"); \
    __uniqueLock.unlock();

#define REACQUIRE_VIEW_LOCK \
    TRACE_LOCK("Reacquiring view unique lock"); \
    __uniqueLock.lock();

class constants
{
protected:
    static std::weak_ptr<isettings_service> _settings;
    static std::weak_ptr<g::iengine> _graphics;
    static std::weak_ptr<a::ivar_console> _varConsole;
    static std::weak_ptr<a::iapp_shell_service> _shellService;
    static std::weak_ptr<g::irender_service> _renderService;
    static std::weak_ptr<a::idebug_console> _debugConsole;
    static std::weak_ptr<iperformance_service> _performanceService;
    static std::weak_ptr<jss::istorage_service> _storageService;
    static std::weak_ptr<bt::itransition_tracking_service> _undoRedoService;
    static std::weak_ptr<a::iui_service> _uiService;
    static std::weak_ptr<jsa::iautomation_service> _automationService;
    static std::weak_ptr<jsm::imemory_tracking_service> _memoryTrackingService;

public:
    // this mutex is used to make sure all the view code (With the exception of the deferred rendering) executes serialized
    // this protects the view data as well as the graphics engine state
    static std::recursive_mutex _viewMutex;
    // this mutex is used to make sure the deferred graphics engine context is protected and that we only allow one deferred
    // rendering at a time (TRACK: we could allow more by having more than one deferred context...)
    static std::recursive_mutex _viewDeferredMutex;
    static const std::shared_ptr<isettings_service> settings() { return _settings.lock(); }
    static const std::shared_ptr<g::iengine> graphics() { return _graphics.lock(); }
    static const std::shared_ptr<a::ivar_console> varConsole() { return _varConsole.lock(); }
    static const std::shared_ptr<a::iapp_shell_service> shellService() { return _shellService.lock(); }
    static const std::shared_ptr<g::irender_service> renderService() { return _renderService.lock(); }
    static const std::shared_ptr<a::idebug_console> debugConsole() { return _debugConsole.lock(); }
    static const std::shared_ptr<iperformance_service> performanceService() { return _performanceService.lock(); }
    static const std::shared_ptr<jss::istorage_service> storageService() { return _storageService.lock(); }
    static const std::shared_ptr<bt::itransition_tracking_service> undoRedoService() { return _undoRedoService.lock(); }
    static const std::shared_ptr<a::iui_service> uiService() { return _uiService.lock(); }
    static const std::shared_ptr<jsa::iautomation_service> automationService() { return _automationService.lock(); }
    static const std::shared_ptr<jsm::imemory_tracking_service> memoryTrackingService() { return _memoryTrackingService.lock(); }

    static void initialize()
    {
        constants::_settings = b::services::get<isettings_service>();
        constants::_graphics = b::services::get<g::iengine>();
        constants::_varConsole = b::services::get<a::ivar_console>();
        constants::_shellService = b::services::get<a::iapp_shell_service>();
        constants::_renderService = b::services::get<g::irender_service>();
        constants::_debugConsole = b::services::get<a::idebug_console>();
        constants::_uiService = b::services::get<a::iui_service>();
        constants::_uiService = b::services::get<a::iui_service>();

        constants::general::initialize();
        constants::references::initialize();
        constants::ink::initialize();
        constants::memory_tracking_settings::initialize();
        constants::test_automation_settings::initialize();
        constants::page_test_automation_settings::initialize();
        constants::stroke_test_automation_settings::initialize();
        constants::playback_settings::initialize();
        constants::performance::initialize();
        constants::page_curling::initialize();
    }

    static void initializePerformanceService()
    {
        constants::_performanceService = b::services::get<iperformance_service>();
    }

    static void initializeMemoryTrackingService()
    {
        constants::_memoryTrackingService = b::services::get<jsm::imemory_tracking_service>();
    }

    static void initializeStorageService()
    {
        constants::_storageService = b::services::get<jss::istorage_service>();
    }

    static void initializeUndoRedoService()
    {
        constants::_undoRedoService = b::services::get<bt::itransition_tracking_service>();
    }

    static void initializeAutomationService()
    {
        constants::_automationService = b::services::get<jsa::iautomation_service>();
    }

    // general settings
    class general
    {
        static const size_t _gridLayoutPageCountY;
        static const size_t _grid2LayoutPageCountY;
        static const b::float32 _pageSlotMargin;
        static const m::point2d _pageWorldDimensions;
        static const b::float32 _pageWorldGridWidth;
        static const m::point2d _pageSlotWorldDimensions;
        static m::point2d _pagePixelDimensions;
        static const b::float32 _maxPagePixelWidth;
        static const m::point2d _thumbnailPixelDimensions;
        static const g::color _directionalLightColor;
        static const m::vector2d _lightOffsetXY;
        static const m::vector3d _lightAngles;
        static const b::float32 _lightDistance;
        static const m::vector2d _lightFocus;
        static const g::color _ambientLightColor;
        static const g::packed_color _backgroundPackedColor;
        static const g::packed_color _shadowPackedColor;
        static const b::float32 _shadowDarkness;
        static const g::color _coverColor;
        static const b::int32 _mipLevels;
        static const b::float32 _cameraDistance;
        static const b::float32 _nearZ;
        static const b::float32 _farZ;
        static const b::wchar* _runtimeVarGroupName;
        static const bool _useWARP;
        static const bool _shutdownDBSessionOnSuspend;
        static const size_t _pageViewCacheCapacity;
        static const size_t _pageModelCacheCapacity;
        static const size_t _pageModelThreadPoolSize;
        static const b::float32 _pageTurnTapRegion;
        static const b::float32 _pageCurlRegion;
        static const b::float32 _maxCurlDistance;
        static const b::float32 _pageCurlEndAngle;
        static const b::float32 _pageCurlTransparencyStartAngle;
        static const b::float32 _pageTurnDragDistance;
        static const b::float32 _inertialDeceleration;
        static const b::float32 _floorDistance;
        static const bool _renderShadows;
        static const view::stack_state _stackState;
        static const bool _traceToFile;
        static const bool _traceToOutput;
        static const view::layout_id _layoutId;
        static const size_t _lastPageNumber;
        static const view::paper_type _paperType;
        static const b::wchar* _sharedPageFileName;
        static const b::wchar* _coverFileName;
        static const m::point2d _backgroundTextureDimensions;
        static const b::float32 _tileFileWidth;
        static const b::wchar* _tileFileName;
        static const b::wchar* _attachmentFileName;
        static const b::wchar* _applicationName;
        static std::vector<std::wstring> _imageFormats;
		static const b::uint32 _thumbnailSaveInterval;

        static b::stopwatch _stopwatch;

    public:
        static struct name {
            static const b::wchar* renderShadows;
            static const b::wchar* zoomLevel;
            static const b::wchar* stackState;
            static const b::wchar* traceToFile;
            static const b::wchar* traceToOutput;
            static const b::wchar* lastPageNumber;
            static const b::wchar* paperType;
            static const b::wchar* backgroundPackedColor;
            static const b::wchar* shadowPackedColor;
            static const b::wchar* shutdownDBSessionOnSuspend;
        };

        enum class id : int
        {
            renderShadows = 0,
            zoomLevel,
            stackState,
            traceToFile,
            traceToOutput,
            lastPageNumber,
            paperType,
            backgroundPackedColor,
            shadowPackedColor,
            shutdownDBSessionOnSuspend,
            count
        };

        static const size_t gridLayoutPageCountY() { return _gridLayoutPageCountY; }
        static const size_t grid2LayoutPageCountY() { return _grid2LayoutPageCountY; }
        static const m::point2d pageWorldDimensions() { return _pageWorldDimensions; }
        static const b::float32 pageWorldGridWidth() { return _pageWorldGridWidth; }
        static const m::point2d pagePixelDimensions() { return _pagePixelDimensions; }
        static const b::float32 maxPagePixelWidth() { return _maxPagePixelWidth; }
        static const m::point2d thumbnailPixelDimensions() { return _thumbnailPixelDimensions; }
        static const b::float32 pageSlotMargin() { return _pageSlotMargin; }
        static const m::point2d pageSlotWorldDimensions() { return _pageSlotWorldDimensions; }
        static const g::color directionalLightColor() { return _directionalLightColor; }
        static const g::color coverColor() { return _coverColor; }
        static const m::vector3d lightAngles() { return _lightAngles; }
        static const m::vector2d lightOffsetXY() { return _lightOffsetXY; }
        static const b::float32 lightDistance() { return _lightDistance; }
        static const m::vector2d lightFocus() { return _lightFocus; }
        static const g::color ambientLightColor() { return _ambientLightColor; }
        static const b::int32 mipLevels() { return _mipLevels; }
        static const b::float32 cameraDistance() { return _cameraDistance; }
        static const b::float32 nearZ() { return _nearZ; }
        static const b::float32 farZ() { return _farZ; }
        static const b::wchar* runtimeVarGroupName() { return _runtimeVarGroupName; }
        static const bool useWARP() { return _useWARP; }
        static const bool shutdownDBSessionOnSuspend() { return constants::settings()->getBool((int)constants::general::id::shutdownDBSessionOnSuspend); }
        static const size_t pageViewCacheCapacity() { return _pageViewCacheCapacity; }
        static const size_t pageModelCacheCapacity() { return _pageModelCacheCapacity; }
        static const size_t pageModelThreadPoolSize() { return _pageModelThreadPoolSize; }
        static const b::float32 shadowDarkness() { return _shadowDarkness; }

        // this is the percentage of size on the right side of the screen where, if the user taps there, it will do a page turn instead of a zoom (e.g. .25 for a 25%)
        static const b::float32 pageTurnTapRegion() { return _pageTurnTapRegion; }

        // percentage of the screen where your finger goes to start a page curl
        static const b::float32 pageCurlRegion() { return _pageCurlRegion; }

        // smoothing distance of page curling
        static const b::float32 maxCurlDistance() { return _maxCurlDistance; }

        static const b::float32 pageCurlEndAngle() { return _pageCurlEndAngle; }
        static const b::float32 pageCurlTransparencyStartAngle() { return _pageCurlTransparencyStartAngle; }

        // the percentage of the screen the user needs to drag the finger for in order for a page turn to happen in fit-to-width mode
        static const b::float32 pageTurnDragDistance() { return _pageTurnDragDistance; }
        
        static const b::float32 inertialDeceleration() { return _inertialDeceleration; }
        static const b::float32 floorDistance() { return _floorDistance; }
        static const b::float32 renderShadows() { return constants::settings()->getBool((int)constants::general::id::renderShadows); }

        static const b::float32 currentTime() { return static_cast<b::float32>(_stopwatch.getElapsedMilliseconds()); }
        static const view::layout_id zoomLevel() { return static_cast<view::layout_id>(constants::settings()->getEnum((int)constants::general::id::zoomLevel)); }
        static const view::stack_state stackState() { return static_cast<view::stack_state>(constants::settings()->getEnum((int)constants::general::id::stackState)); }
        static const bool traceToFile() { return static_cast<bool>(constants::settings()->getBool((int)constants::general::id::traceToFile)); }
        static const bool traceToOutput() { return static_cast<bool>(constants::settings()->getBool((int)constants::general::id::traceToOutput)); }
        static const size_t lastPageNumber() { return static_cast<size_t>(constants::settings()->getInt((int)constants::general::id::lastPageNumber)); }
        static const view::paper_type paperType() { return static_cast<view::paper_type>(constants::settings()->getEnum((int)constants::general::id::paperType)); }
        static const b::wchar* paperTypeFile();
        static const b::wchar* sharedPageFileName() { return _sharedPageFileName; }
        static const b::wchar* coverFileName() { return _coverFileName; }
        static const m::point2d backgroundTextureDimensions() { return _backgroundTextureDimensions; }
        static const b::float32 tileFileWidth() { return _tileFileWidth; }
        static const b::wchar* tileFileName() { return _tileFileName; }
        static const b::wchar* attachmentFileName() { return _attachmentFileName; }
        static const b::wchar* applicationName() { return _applicationName; }
        static const std::vector<std::wstring>& imageFormats() { return _imageFormats; }
		static const b::uint32 thumbnailSaveInterval() { return _thumbnailSaveInterval; }

        static const g::packed_color backgroundPackedColor() { return (g::packed_color)constants::settings()->getInt((int)constants::general::id::backgroundPackedColor); }
        static const g::packed_color shadowPackedColor() { return (g::packed_color)constants::settings()->getInt((int)constants::general::id::shadowPackedColor); }

        static void initialize()
        {
            // calculate page pixel size based on screen resolution
            _pagePixelDimensions.x = min(constants::shellService()->windowSize().x, constants::general::maxPagePixelWidth());
            b::float32 aspectRatio = _pageWorldDimensions.y / _pageWorldDimensions.x;
            _pagePixelDimensions.y = floor(aspectRatio * _pagePixelDimensions.x);

            BAJA_TRACE(boost::format("page pixel dimensions: width: %i, height: %i") % _pagePixelDimensions.x % _pagePixelDimensions.y);

            // add supported image formats
            constants::general::_imageFormats.push_back(L".png");
            constants::general::_imageFormats.push_back(L".jpg");
            constants::general::_imageFormats.push_back(L".tif");
            constants::general::_imageFormats.push_back(L".bmp");
            constants::general::_imageFormats.push_back(L".gif");
            constants::general::_imageFormats.push_back(L".tga");
            constants::general::_imageFormats.push_back(L".dds");

            _stopwatch.reset();

            constants::settings()->registerBool(
                (int)constants::general::id::renderShadows,
                constants::general::name::renderShadows,
                _renderShadows
                );

            static const b::wchar* zoom_level_names[] = { L"grid", L"full", L"fit to width" };
            static const int zoom_level_values[] = { 
                static_cast<int>(view::layout_id::grid), 
                static_cast<int>(view::layout_id::full),
                static_cast<int>(view::layout_id::fitToWidth)
                };

            constants::settings()->registerEnum(
                (int)constants::general::id::zoomLevel,
                constants::general::name::zoomLevel,
                zoom_level_values,
                zoom_level_names,
                static_cast<b::uint32>(3),
                static_cast<int>(_layoutId)
                );

            static const b::wchar* stack_state_names[] = { L"expanded", L"collapsed" };
            static const int stack_state_values[] = { 
                static_cast<int>(view::stack_state::expanded), 
                static_cast<int>(view::stack_state::stacked),
                };

            constants::settings()->registerEnum(
                (int)constants::general::id::stackState,
                constants::general::name::stackState,
                stack_state_values,
                stack_state_names,
                static_cast<b::uint32>(2),
                static_cast<int>(_stackState)
                );
        
            constants::settings()->registerBool(
                (int)constants::general::id::traceToFile,
                constants::general::name::traceToFile,
                _traceToFile
                );

            constants::settings()->registerBool(
                (int)constants::general::id::traceToOutput,
                constants::general::name::traceToOutput,
                _traceToOutput
                );

            static const b::wchar* paper_type_names[] = { L"paper 0", L"paper 1", L"paper 2", L"paper 3", L"paper 4" };
            static const int paper_type_values[] = { 
                static_cast<int>(view::paper_type::paper_0), 
                static_cast<int>(view::paper_type::paper_1),
                static_cast<int>(view::paper_type::paper_2),
                static_cast<int>(view::paper_type::paper_3),
                static_cast<int>(view::paper_type::paper_4),
                };

            constants::settings()->registerEnum(
                (int)constants::general::id::paperType,
                constants::general::name::paperType,
                paper_type_values,
                paper_type_names,
                static_cast<b::uint32>(view::paper_type::count),
                static_cast<int>(_paperType)
                );

            constants::settings()->registerInt(
                (int)constants::general::id::lastPageNumber,
                constants::general::name::lastPageNumber,
                0, // min
                INT_MAX, // max
                1, // step
                static_cast<int>(_lastPageNumber) // default
                );

            constants::settings()->registerInt(
                (int)constants::general::id::backgroundPackedColor,
                constants::general::name::backgroundPackedColor,
                -INT_MAX, // min
                INT_MAX, // max
                1, // step
                static_cast<int>(_backgroundPackedColor) // default
                );

            constants::settings()->registerInt(
                (int)constants::general::id::shadowPackedColor,
                constants::general::name::shadowPackedColor,
                -INT_MAX, // min
                INT_MAX, // max
                1, // step
                static_cast<int>(_shadowPackedColor) // default
                );

            constants::settings()->registerBool(
                (int)constants::general::id::shutdownDBSessionOnSuspend,
                constants::general::name::shutdownDBSessionOnSuspend,
                _shutdownDBSessionOnSuspend
                );
        }
    };

    // animation settings
    class animations
    {
        static const b::float32 _startingScale;
        static const b::float32 _startingDuration; 
        static const m::point2d _startingAcceleration;
        static const m::point2d _pageMoveAcceleration;
        static const b::float32 _pageMoveDuration;
        static const b::float32 _rotationDuration;
        static const b::float32 _pageSelectDuration;
        static const b::float32 _pageDeleteDuration;
        static const b::float32 _tapCurlDuration;
        static const m::point2d _defaultAcceleration;
        static const b::float32 _quickDuration;
        static const b::float32 _stackExpansionDuration;
        static const m::point2d _stackExpansionAcceleration;
        static const b::float32 _transitionDuration;
        static const m::point2d _transitionAcceleration;
        static const b::float32 _scrollbarVisibilityDuration;

    public:
        static const b::float32 startingScale() { return _startingScale; }
        static const b::float32 startingDuration() { return _startingDuration; }
        static const m::point2d startingAcceleration() { return _startingAcceleration; }
        static const b::float32 pageMoveDuration() { return _pageMoveDuration; }
        static const m::point2d pageMoveAcceleration() { return _pageMoveAcceleration; }
        static const b::float32 rotationDuration() { return _rotationDuration; }
        static const b::float32 pageSelectDuration() { return _pageSelectDuration; }
        static const b::float32 pageDeleteDuration() { return _pageDeleteDuration; }
        static const b::float32 tapCurlDuration() { return _tapCurlDuration; }
        static const m::point2d defaultAcceleration() { return _defaultAcceleration;  }
        static const b::float32 quickDuration() { return _quickDuration; }
        static const b::float32 stackExpansionDuration() { return _stackExpansionDuration; }
        static const m::point2d stackExpansionAcceleration() { return _stackExpansionAcceleration; }
        static const b::float32 transitionDuration() { return _transitionDuration; }
        static const m::point2d transitionAcceleration() { return _transitionAcceleration; }
        static const b::float32 scrollbarVisibilityDuration() { return _scrollbarVisibilityDuration; }
        static void initialize() {}
    };

    // debugging references
    class references
    {
        static const b::int32 _debugConsoleLineCount;
        static const b::wchar* _fontName;
        static const b::int32 _fontSize;
        static const b::int32 _fontSizeSmall;
        static const bool _showAxes;
        static const bool _showFPS;
        static const bool _showViewInfo;
        static const bool _showTouchPoints;
        static const bool _showKeys;
        static const b::int32 _floorTileCount;
        static const b::float32 _floorTileSize;

    public:
        static struct name {
            static const b::wchar* showAxes;
            static const b::wchar* showFPS;
            static const b::wchar* showViewInfo;
            static const b::wchar* showTouchPoints;
            static const b::wchar* showKeys;
            static const b::wchar* floorTileCount;
            static const b::wchar* floorTileSize;
        };

        enum class id : int
        {
            showAxes = constants::general::id::count + 1,
            showFPS,
            showViewInfo,
            showTouchPoints,
            showKeys,
            floorTileCount,
            floorTileSize,
            count
        };

        static const b::int32 debugConsoleLineCount() { return _debugConsoleLineCount; }
        static const bool showAxes() { return constants::settings()->getBool((int)constants::references::id::showAxes); }
        static const bool showFPS() { return constants::settings()->getBool((int)constants::references::id::showFPS); }
        static const bool showViewInfo() { return constants::settings()->getBool((int)constants::references::id::showViewInfo); }
        static const bool showTouchPoints() { return constants::settings()->getBool((int)constants::references::id::showTouchPoints); }
        static const bool showKeys() { return constants::settings()->getBool((int)constants::references::id::showKeys); }
        static const b::int32 floorTileCount() { return constants::settings()->getInt((int)constants::references::id::floorTileCount); }
        static const b::float32 floorTileSize() { return constants::settings()->getFloat((int)constants::references::id::floorTileSize); }
        static const b::wchar* fontName() { return _fontName; }
        static const b::int32 fontSize() { return _fontSize; }
        static const b::int32 fontSizeSmall() { return _fontSizeSmall; }

        static void initialize()
        {
            constants::settings()->registerBool(
                (int)constants::references::id::showAxes,
                constants::references::name::showAxes,
                _showAxes
                );

            constants::settings()->registerBool(
                (int)constants::references::id::showFPS,
                constants::references::name::showFPS,
                _showFPS
                );

            constants::settings()->registerBool(
                (int)constants::references::id::showViewInfo,
                constants::references::name::showViewInfo,
                _showViewInfo
                );

            constants::settings()->registerBool(
                (int)constants::references::id::showTouchPoints,
                constants::references::name::showTouchPoints,
                _showTouchPoints
                );

            constants::settings()->registerBool(
                (int)constants::references::id::showKeys,
                constants::references::name::showKeys,
                _showKeys
                );

            constants::settings()->registerInt(
                (int)constants::references::id::floorTileCount,
                constants::references::name::floorTileCount,
                1, // min
                1000, // max
                10, // step
                _floorTileCount  // default
                );

            constants::settings()->registerFloat(
                (int)constants::references::id::floorTileSize,
                constants::references::name::floorTileSize,
                0.2f, // min
                10.f, // max
                0.2f, // step
                _floorTileSize  // default
                );
        };
    };

    // memory tracking settings
    class memory_tracking_settings
    {
    private:
        static const bool _showMonitors;
    public:
        static struct name {
            static const b::wchar* showMonitors;
        };

        enum class id : int
        {
            showMonitors = constants::references::id::count + 1,
            count
        };

    public:
        static const bool showMonitors() { return constants::settings()->getBool((int)constants::memory_tracking_settings::id::showMonitors); }
        static void initialize()
        {
            constants::settings()->registerBool(
                (int)constants::memory_tracking_settings::id::showMonitors,
                constants::memory_tracking_settings::name::showMonitors,
                _showMonitors
                );
        }
    };

    // test automation settings
    class test_automation_settings
    {
    private:
        static const bool _pageStressTestEnabled;
        static const bool _strokeStressTestEnabled;
    public:
        static struct name {
            static const b::wchar* pageStressTestEnabled;
            static const b::wchar* strokeStressTestEnabled;
        };

        enum class id : int
        {
            pageStressTestEnabled = constants::memory_tracking_settings::id::count + 1,
            strokeStressTestEnabled,
            count
        };

    public:
        static const bool pageStressTestEnabled() { return constants::settings()->getBool((int)constants::test_automation_settings::id::pageStressTestEnabled); }
        static const bool strokeStressTestEnabled() { return constants::settings()->getBool((int)constants::test_automation_settings::id::strokeStressTestEnabled); }
        static void initialize()
        {
            constants::settings()->registerBool(
                (int)constants::test_automation_settings::id::pageStressTestEnabled,
                constants::test_automation_settings::name::pageStressTestEnabled,
                _pageStressTestEnabled
                );
            constants::settings()->registerBool(
                (int)constants::test_automation_settings::id::strokeStressTestEnabled,
                constants::test_automation_settings::name::strokeStressTestEnabled,
                _strokeStressTestEnabled
                );
        }
    };

    // page stress test
    class page_test_automation_settings
    {
    private:
        static const int _pageCount;
        static const int _sourcePhotoCount;
        static const int _photoCountPerPage;
        static const int _postPhotoPause;
        static const int _strokeCountPerPage;
        static const int _postPageCreatePause;
        static const int _postSymbolPause;
        static const int _postIterationPause;
        static const int _fontSize;
        static const int _fontThickness;
    public:
        static struct name {
            static const b::wchar* pageCount;
            static const b::wchar* sourcePhotoCount;
            static const b::wchar* photoCountPerPage;
            static const b::wchar* postPhotoPause;
            static const b::wchar* strokeCountPerPage;
            static const b::wchar* postSymbolPause;
            static const b::wchar* postIterationPause;
            static const b::wchar* fontSize;
            static const b::wchar* fontThickness;
        };

        enum class id : int
        {
            pageCount = constants::test_automation_settings::id::count + 1,
            sourcePhotoCount,
            photoCountPerPage,
            postPhotoPause,
            strokeCountPerPage,
            postSymbolPause,
            postIterationPause,
            fontSize,
            fontThickness,
            count
        };

    public:
        static const int pageCount() { return constants::settings()->getInt((int)constants::page_test_automation_settings::id::pageCount); }
        static const int sourcePhotoCount() { return constants::settings()->getInt((int)constants::page_test_automation_settings::id::sourcePhotoCount); }
        static const int photoCountPerPage() { return constants::settings()->getInt((int)constants::page_test_automation_settings::id::photoCountPerPage); }
        static const int postPhotoPause() { return constants::settings()->getInt((int)constants::page_test_automation_settings::id::postPhotoPause); }
        static const int strokeCountPerPage() { return constants::settings()->getInt((int)constants::page_test_automation_settings::id::strokeCountPerPage); }
        static const int postSymbolPause() { return constants::settings()->getInt((int)constants::page_test_automation_settings::id::postSymbolPause); }
        static const int postIterationPause() { return constants::settings()->getInt((int)constants::page_test_automation_settings::id::postIterationPause); }
        static const int fontSize() { return constants::settings()->getInt((int)constants::page_test_automation_settings::id::fontSize); }
        static const int fontThickness() { return constants::settings()->getInt((int)constants::page_test_automation_settings::id::fontThickness); }
        static void initialize()
        {
            constants::settings()->registerInt(
                (int)constants::page_test_automation_settings::id::pageCount,
                constants::page_test_automation_settings::name::pageCount,
                0,
                2000,
                20,
                20
                );
            constants::settings()->registerInt(
                (int)constants::page_test_automation_settings::id::sourcePhotoCount,
                constants::page_test_automation_settings::name::sourcePhotoCount,
                1,
                10,
                1,
                10
                );
            constants::settings()->registerInt(
                (int)constants::page_test_automation_settings::id::photoCountPerPage,
                constants::page_test_automation_settings::name::photoCountPerPage,
                0,
                10,
                1,
                5
                );
            constants::settings()->registerInt(
                (int)constants::page_test_automation_settings::id::postPhotoPause,
                constants::page_test_automation_settings::name::postPhotoPause,
                20,
                1000,
                10,
                100
                );
            constants::settings()->registerInt(
                (int)constants::page_test_automation_settings::id::strokeCountPerPage,
                constants::page_test_automation_settings::name::strokeCountPerPage,
                0,
                3000,
                50,
                200
                );
            constants::settings()->registerInt(
                (int)constants::page_test_automation_settings::id::postSymbolPause,
                constants::page_test_automation_settings::name::postSymbolPause,
                0,
                1000,
                5,
                5
                );
            constants::settings()->registerInt(
                (int)constants::page_test_automation_settings::id::postIterationPause,
                constants::page_test_automation_settings::name::postIterationPause,
                0,
                1000,
                5,
                150
                );
            constants::settings()->registerInt(
                (int)constants::page_test_automation_settings::id::fontSize,
                constants::page_test_automation_settings::name::fontSize,
                1,
                5,
                1,
                1
                );
            constants::settings()->registerInt(
                (int)constants::page_test_automation_settings::id::fontThickness,
                constants::page_test_automation_settings::name::fontThickness,
                1,
                5,
                1,
                1
                );
        }
    };

    // page stress test
    class stroke_test_automation_settings
    {
    private:
        static const int _strokeCount;
        static const int _postPageCreatePause;
        static const int _postSymbolPause;
        static const int _fontSize;
        static const int _fontThickness;
    public:
        static struct name {
            static const b::wchar* strokeCount;
            static const b::wchar* postSymbolPause;
            static const b::wchar* fontSize;
            static const b::wchar* fontThickness;
        };

        enum class id : int
        {
            strokeCount = constants::page_test_automation_settings::id::count + 1,
            postSymbolPause,
            fontSize,
            fontThickness,
            count
        };

    public:
        static const int strokeCount() { return constants::settings()->getInt((int)constants::stroke_test_automation_settings::id::strokeCount); }
        static const int postSymbolPause() { return constants::settings()->getInt((int)constants::stroke_test_automation_settings::id::postSymbolPause); }
        static const int fontSize() { return constants::settings()->getInt((int)constants::stroke_test_automation_settings::id::fontSize); }
        static const int fontThickness() { return constants::settings()->getInt((int)constants::stroke_test_automation_settings::id::fontThickness); }
        static void initialize()
        {
            constants::settings()->registerInt(
                (int)constants::stroke_test_automation_settings::id::strokeCount,
                constants::stroke_test_automation_settings::name::strokeCount,
                0,
                2000,
                50,
                200
                );
            constants::settings()->registerInt(
                (int)constants::stroke_test_automation_settings::id::postSymbolPause,
                constants::stroke_test_automation_settings::name::postSymbolPause,
                0,
                1000,
                5,
                5
                );
            constants::settings()->registerInt(
                (int)constants::stroke_test_automation_settings::id::fontSize,
                constants::stroke_test_automation_settings::name::fontSize,
                1,
                5,
                1,
                1
                );
            constants::settings()->registerInt(
                (int)constants::stroke_test_automation_settings::id::fontThickness,
                constants::stroke_test_automation_settings::name::fontThickness,
                1,
                5,
                1,
                1
                );
        }
    };

    // playback settings
    class playback_settings
    {
    private:
        static const bool _maxSpeed;
        static const float _speed;
        static const int _postPageCreatePause;
    public:
        static struct name {
            static const b::wchar* maxSpeed;
            static const b::wchar* speed;
        };

        enum class id : int
        {
            speed = constants::stroke_test_automation_settings::id::count + 1,
            maxSpeed,
            count
        };

    public:
        static const bool maxSpeed() { return constants::settings()->getBool((int)constants::playback_settings::id::maxSpeed); }
        static const float speed() { return constants::settings()->getFloat((int)constants::playback_settings::id::speed); }
        static void initialize()
        {
            constants::settings()->registerBool(
                (int)constants::playback_settings::id::maxSpeed,
                constants::playback_settings::name::maxSpeed,
                _maxSpeed
                );
            constants::settings()->registerFloat(
                (int)constants::playback_settings::id::speed,
                constants::playback_settings::name::speed,
                0.1f,
                10.0f,
                0.1f,
                1.0f
                );
        }
    };

    // performance settings
    class performance
    {
    private:
        static const bool _showMonitors;
        static const bool _useAmp;
        static const int _sampleCount;

    public:
        static struct name {
            static const b::wchar* useAmp;
            static const b::wchar* showMonitors;
            static const b::wchar* reset;
        };

        enum class id : int
        {
            useAmp = constants::playback_settings::id::count + 1,
            showMonitors,
            reset,
            count
        };

    public:
        static const int sampleCount() { return _sampleCount; }
        static const bool showMonitors() { return constants::settings()->getBool((int)constants::performance::id::showMonitors); }
        static const bool useAmp() 
        {
            return constants::settings()->getBool((int)constants::performance::id::useAmp); 
        }

        static void initialize()
        { 
            constants::settings()->registerBool(
                (int)constants::performance::id::useAmp,
                constants::performance::name::useAmp,
                _useAmp
                );
            constants::settings()->registerBool(
                (int)constants::performance::id::showMonitors,
                constants::performance::name::showMonitors,
                _showMonitors
                );
            constants::settings()->registerInt(
                (int)constants::performance::id::reset,
                constants::performance::name::reset,
                1, // min
                1, // max
                1, // step
                1  // default
                );
        }
    };

    // ink settings
    class ink
    {
    public:
        // enums
        enum class render_technique : int
        {
            normal = 1,
            wireframe = 2,

            count = 2
        };

        enum class ink_color : int
        {
            black = 1,
            red = 2,
            highlighter = 3,
            pressure = 4,
            blue = 5,
            green = 6,
            pink = 7,

            count = 7
        };

        enum class ink_tool : int
        {
            pen = 1,
            eraser = 2,

            count = 2
        };

    private:
        static const b::float32 _baseWidth;
        static const int _renderTechnique;
        static const int _inkColor;
        static const b::float32 _pointMinimumDistance; 
        static const b::float32 _pointMinimumPressureDifference;
        static const bool _smooth;
        static const bool _smoothVectorized;
        static const int _inkTool;
        static const b::uint32 _eraserSizeInPixels;
        static const b::uint32 _maxVertexCount;
        static const b::uint32 _maxMemoryVertexCount;
        static const b::uint32 _maxMemoryVertexCountExpansionRatio;
        static const b::uint32 _slicesForRoundStamp;
        static const b::float32 _smoothingInterval;
        static const bool _touchInputMode;
        static const bool _showTouchInputToggle;
        static const bool _debuggingMode;

    public:
        static struct name {
            static const b::wchar* baseWidth;
            static const b::wchar* renderTechnique;
            static const b::wchar* inkColor;
            static const b::wchar* pointMinimumDistance;
            static const b::wchar* rebuildCache;
            static const b::wchar* smooth;
            static const b::wchar* smoothVectorized;
            static const b::wchar* inkTool;
            static const b::wchar* eraserSizeInPixels;
            static const b::wchar* slicesForRoundStamp;
            static const b::wchar* smoothingInterval;
            static const b::wchar* touchInputMode;
            static const b::wchar* showTouchInputToggle;
            static const b::wchar* debuggingMode;
        };

        enum class id : int
        {
            baseWidth = constants::performance::id::count + 1,
            rebuildCache,
            renderTechnique,
            inkColor,
            pointMinimumDistance,
            smooth,
            smoothVectorized,
            inkTool,
            eraserSizeInpixels,
            slicesForRoundStamp,
            smoothingInterval,
            touchInputMode,
            showTouchInputToggle,
            debuggingMode,
            count
        };

    public:
        static const b::float32 baseWidth() { return constants::settings()->getFloat((int)constants::ink::id::baseWidth); }
        static const render_technique renderTechnique() { return static_cast<render_technique>(constants::settings()->getEnum((int)constants::ink::id::renderTechnique)); }
        static const ink_color inkColor() { return static_cast<ink_color>(constants::settings()->getEnum((int)constants::ink::id::inkColor)); }
        static const b::float32 pointMinimumDistance() { return constants::settings()->getFloat((int)constants::ink::id::pointMinimumDistance); }
        static const b::float32 pointMinimumPressureDifference() { return _pointMinimumPressureDifference; }
        static const bool smooth() { return constants::settings()->getBool((int)constants::ink::id::smooth); }
        static const bool smoothVectorized() { return constants::settings()->getBool((int)constants::ink::id::smoothVectorized); }
        static const b::uint32 eraserSizeInPixels() { return _eraserSizeInPixels; }
        static const ink_tool inkTool() { return static_cast<ink_tool>(constants::settings()->getEnum((int)constants::ink::id::inkTool)); }
        static const b::uint32 maxVertexCount() { return _maxVertexCount; }
        static const b::uint32 maxMemoryVertexCount() { return _maxMemoryVertexCount; }
        static const b::uint32 maxMemoryVertexCountExpansionRatio() { return _maxMemoryVertexCountExpansionRatio; }
        static const b::uint32 slicesForRoundStamp() { return _slicesForRoundStamp; }
        static const b::float32 smoothingInterval() { return constants::settings()->getFloat((int)constants::ink::id::smoothingInterval); }
        static const bool touchInputMode() { return constants::settings()->getBool((int)constants::ink::id::touchInputMode); }
        static const bool showTouchInputToggle() { return constants::settings()->getBool((int)constants::ink::id::showTouchInputToggle); }
        static const bool debuggingMode() { return constants::settings()->getBool((int)constants::ink::id::debuggingMode); }

        static void initialize()
        { 
            constants::settings()->registerFloat(
                (int)constants::ink::id::baseWidth,
                constants::ink::name::baseWidth,
                0.01f,	// min
                1.0f,	// max
                0.005f,	// step
                _baseWidth	// default
                );

            constants::settings()->registerInt(
                (int)constants::ink::id::rebuildCache,
                constants::ink::name::rebuildCache,
                1, // min
                1, // max
                1, // step
                1  // default
                );

            constants::settings()->registerBool(
                (int)constants::ink::id::smooth,
                constants::ink::name::smooth,
                _smooth
                );

            constants::settings()->registerBool(
                (int)constants::ink::id::touchInputMode,
                constants::ink::name::touchInputMode,
                _touchInputMode
                );

            constants::settings()->registerBool(
                (int)constants::ink::id::showTouchInputToggle,
                constants::ink::name::showTouchInputToggle,
                _touchInputMode
                );

            constants::settings()->registerBool(
                (int)constants::ink::id::smoothVectorized,
                constants::ink::name::smoothVectorized,
                _smoothVectorized
                );

            constants::settings()->registerBool(
                (int)constants::ink::id::debuggingMode,
                constants::ink::name::debuggingMode,
                _debuggingMode
                );

            static const b::wchar* render_technique_names[] = { L"normal", L"wireframe" };
            static const int render_technique_values[] = { 1, 2 };

            constants::settings()->registerEnum(
                (int)constants::ink::id::renderTechnique,
                constants::ink::name::renderTechnique,
                render_technique_values,
                render_technique_names,
                static_cast<b::uint32>(render_technique::count),
                _renderTechnique
                );

            static const b::wchar* ink_color_names[] = { L"black", L"red", L"highlighter", L"pressure gradient", L"blue", L"green", L"pink" };
            static const int ink_color_values[] = { 1, 2, 3, 4, 5, 6, 7 };

            constants::settings()->registerEnum(
                (int)constants::ink::id::inkColor,
                constants::ink::name::inkColor,
                ink_color_values,
                ink_color_names,
                static_cast<b::uint32>(ink_color::count),
                _inkColor
                );

            static const b::wchar* ink_tool_names[] = { L"pen", L"eraser" };
            static const int ink_tool_values[] = { 1, 2 };

            constants::settings()->registerEnum(
                (int)constants::ink::id::inkTool,
                constants::ink::name::inkTool,
                ink_tool_values,
                ink_tool_names,
                static_cast<b::uint32>(ink_tool::count),
                _inkTool
                );

            constants::settings()->registerFloat(
                (int)constants::ink::id::pointMinimumDistance,
                constants::ink::name::pointMinimumDistance,
                0.0000001f, // min
                2.f,		// max
                0.005f,	// step
                _pointMinimumDistance	// default
                );

            constants::settings()->registerFloat(
                (int)constants::ink::id::smoothingInterval,
                constants::ink::name::smoothingInterval,
                0.000001f, // min
                2.f,		// max
                0.005f,	// step
                _smoothingInterval	// default
                );
        }
    };
    
    // page curling settings
    class page_curling
    {
    private:
        static const b::float32 _curlTheta;
        static const b::float32 _curlAy;
        static const b::float32 _curlAngle;
        static const b::float32 _curlConicContribution;

    public:
        static struct name {
            static const b::wchar* curlTheta;
            static const b::wchar* curlAy;
            static const b::wchar* curlAngle;
            static const b::wchar* curlConicContribution;
        };

        enum class id : int
        {
            curlTheta = constants::ink::id::count + 1,
            curlAy,
            curlAngle,
            curlConicContribution,
            count
        };

    public:
        static const b::float32 curlTheta() { return constants::settings()->getFloat((int)constants::page_curling::id::curlTheta); }
        static const b::float32 curlAy() { return constants::settings()->getFloat((int)constants::page_curling::id::curlAy); }
        static const b::float32 curlAngle() { return constants::settings()->getFloat((int)constants::page_curling::id::curlAngle); }
        static const b::float32 curlConicContribution() { return constants::settings()->getFloat((int)constants::page_curling::id::curlConicContribution); }

        static void initialize()
        {
            constants::settings()->registerFloat(
                (int)constants::page_curling::id::curlTheta,
                constants::page_curling::name::curlTheta,
                0.f,	// min
                90.0f,	// max
                1.0f,	// step
                _curlTheta	// default
                );

            constants::settings()->registerFloat(
                (int)constants::page_curling::id::curlAy,
                constants::page_curling::name::curlAy,
                -100.f,	// min
                0.0f,	// max
                0.5f,	// step
                _curlAy	// default
                );

            constants::settings()->registerFloat(
                (int)constants::page_curling::id::curlAngle,
                constants::page_curling::name::curlAngle,
                0.f,	// min
                90.0f,	// max
                1.0f,	// step
                _curlAngle	// default
                );

            constants::settings()->registerFloat(
                (int)constants::page_curling::id::curlConicContribution,
                constants::page_curling::name::curlConicContribution,
                -1.0f,	// min
                1.0f,	// max
                0.1f,	// step
                _curlConicContribution	// default
                );
        }
    };
};

} // namespace journal